package com.mcjtf.prefix.commands;

import com.mcjtf.prefix.commands.SubCommand.SenderType;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class MainCommand implements CommandExecutor {
    private Map<String, Method> commands = new HashMap<String, Method>();
    // <子命令,方法>
    private Map<String, SubCommand> subCommands = new HashMap<String, SubCommand>();
    private TreeMap<String, String> cmdDesc = new TreeMap<String, String>();
    private Map<String, Object> cmdCls = new HashMap<String, Object>();
    private String mainCmd;
    private JavaPlugin plugin;

    public MainCommand(String cmd, JavaPlugin plugin) {
        this.mainCmd = cmd;
        this.plugin = plugin;
        Bukkit.getServer().getPluginCommand(cmd).setExecutor(this);
    }

    public MainCommand register(Object instance) {
        registerMethods(instance.getClass(), null, instance);
        return this;
    }

    public List<SubCommand> registerAndReturn(Object instance) {
        return registerMethods(instance.getClass(), null, instance);
    }

    private List<SubCommand> registerMethods(Class<?> cls, Method parent, Object instance) {
        plugin.getCommand(mainCmd).setExecutor(this);

        List<SubCommand> registered = new ArrayList<SubCommand>();
        if (parent != null) {
            if (parent.isAnnotationPresent(SubCommand.class)) {
                SubCommand cmd = parent.getAnnotation(SubCommand.class);
                String helpMsg = msg("&6/" + mainCmd + " &b" + cmd.cmd() + " " + cmd.usage() + " &a" + cmd.desc());

                registered.add(cmd);
                commands.put(cmd.cmd().toLowerCase(), parent);
                cmdDesc.put(cmd.cmd().toLowerCase(), helpMsg);
                subCommands.put(cmd.cmd().toLowerCase(), cmd);
                if (instance != null) {
                    cmdCls.put(cmd.cmd().toLowerCase(), instance);
                } else {
                    cmdCls.put(cmd.cmd().toLowerCase(), cls);
                }
            }
        } else {
            for (Method method : cls.getDeclaredMethods()) {
                if (!method.isAnnotationPresent(SubCommand.class))
                    continue;
                SubCommand cmd = method.getAnnotation(SubCommand.class);
                String helpMsg = msg("&6/" + mainCmd + " &b" + cmd.cmd() + " " + cmd.usage() + " &6- &a" + cmd.desc());

                registered.add(cmd);
                commands.put(cmd.cmd().toLowerCase(), method);
                cmdDesc.put(cmd.cmd().toLowerCase(), helpMsg);
                subCommands.put(cmd.cmd().toLowerCase(), cmd);
                if (instance != null) {
                    cmdCls.put(cmd.cmd().toLowerCase(), instance);
                } else {
                    cmdCls.put(cmd.cmd().toLowerCase(), cls);
                }
            }
        }
        return registered;
    }

    public void sendHelpMsg(CommandSender sender) {
        sender.sendMessage(msg("&e---------------  &a帮助  &e---------------"));
        boolean isPlayer = (sender instanceof Player);
        cmdDesc.forEach((key, desc) -> {
            if (!subCommands.containsKey(key)) {
                return;
            }
            SubCommand sub = subCommands.get(key);
            if (isPlayer && sub.sender() == SenderType.CONSOLE) {
                return;
            }
            if (!sub.permission().equalsIgnoreCase("") && !sender.hasPermission(sub.permission())) {
                return;
            }
            sender.sendMessage(desc);
        });
    }

    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (args.length == 0 || args[0].equalsIgnoreCase("help")) {
            sendHelpMsg(sender);
            return true;
        }
        if (!commands.containsKey(args[0].toLowerCase())) {
            sender.sendMessage(msg("&c[警告] &a子命令&c" + args[0] + "&a不存在."));
            sender.sendMessage(msg("&b[提示] &a你可以输入&6/" + mainCmd + " help &a查看帮助."));
            return true;
        }
        SubCommand subCmd = subCommands.get(args[0].toLowerCase());
        if (!(sender instanceof Player) && subCmd.sender() == SenderType.PLAYER) {
            sender.sendMessage("此指令无法在控制台执行");
            return true;
        }
        if (!subCmd.permission().equalsIgnoreCase("") && !sender.hasPermission(subCmd.permission())) {
            sender.sendMessage(msg("&c&c[警告] 你没有权限执行此命令!"));
            return true;
        }
        if (args.length < subCmd.min() || args.length > subCmd.max()) {
            sender.sendMessage(msg("&c[警告] &a输入的参数数量错误(最小&e" + subCmd.min() + "&a最大&e" + subCmd.max() + "&a)"));
            sender.sendMessage(msg("&b[提示] " + cmdDesc.get(args[0].toLowerCase())));
            return true;
        }
        Method method = commands.get(args[0].toLowerCase());
        try {
            if (subCmd.sender() == SenderType.PLAYER) {
                Player p = (Player) sender;
                method.invoke(cmdCls.get(args[0].toLowerCase()), p, args);
            } else {
                method.invoke(cmdCls.get(args[0].toLowerCase()), sender, args);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return true;
    }

    private String msg(String msg) {
        return msg == null ? "" : msg.replace('&', '§');
    }
}
